#include <QFile>
#include <QString>
#include <QTextStream>
#include <QPainter>
#include "math.h"

#include "engine.h"
#include "minion.h"

Minion::Minion(const QString &Filename, double DefaultOrientation)
{
    QFile file(Filename);
    QString line;
    if(file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream stream(&file);
        while(!stream.atEnd())
        {
            line = stream.readLine();
            int pos = line.indexOf(":");
            if(pos == -1) continue;

            QString parameter = line.mid(0, pos);
            QString value = line.mid(pos + 1);

            // if(parameter == "Team") Team = value.toInt();
            if(parameter == "PowerFactor") PowerFactor = value.toInt();
            else if(parameter == "ArmorFactor") ArmorFactor = value.toInt();
            else if(parameter == "HealthFactor") HealthFactor = value.toInt();
            else if(parameter == "FireRateFactor") FireRateFactor = value.toDouble();
            else if(parameter == "SightFactor") SightFactor = value.toInt();
            else if(parameter == "RangeFactor") RangeFactor = value.toInt();
            else if(parameter == "SpeedFactor") SpeedFactor = value.toDouble();
            else if(parameter == "BulletSpeedFactor") BulletSpeedFactor = value.toDouble();
            else if(parameter == "RotationSpeedFactor") RotationSpeedFactor = value.toDouble();
            else if(parameter == "TurretRotationSpeedFactor") TurretRotationSpeedFactor = value.toDouble();
            else if(parameter == "Size") Size = value.toInt();
            else if(parameter == "PictureBase") PictureBase = Engine::LoadPicture(value);
            else if(parameter == "PictureTurret") PictureTurret = Engine::LoadPicture(value);
            else if(parameter == "PictureBullet") PictureBullet = Engine::LoadPicture(value);
            else if(parameter == "Ability1") Ability1 = LoadAbility(value);
            else if(parameter == "Ability2") Ability2 = LoadAbility(value);
            else if(parameter == "Ability3") Ability3 = LoadAbility(value);
            // else if(parameter == "Level") Level = value.toInt();
            else if(parameter == "Power") Power = value.toInt();
            else if(parameter == "Armor") Armor = value.toInt();
            else if(parameter == "MaxHealth") Health = MaxHealth = value.toInt();
            else if(parameter == "FireRate") FireRate = value.toDouble();
            else if(parameter == "Sight") Sight = value.toInt();
            else if(parameter == "Range") Range = value.toInt();
            else if(parameter == "Speed") Speed = value.toDouble();
            else if(parameter == "BulletSpeed") BulletSpeed = value.toDouble();
            else if(parameter == "RotationSpeed") RotationSpeed = value.toDouble();
            else if(parameter == "TurretRotationSpeed") TurretRotationSpeed = value.toDouble();
            // else if(parameter == "Experience") Experience = value.toInt();
            // else if(parameter == "TimeToRespawn") TimeToRespawn = value.toInt();
            else if(parameter == "Flags") Flags = (MinionFlags)value.toInt();
            // else if(parameter == "X") X = value.toDouble();
            // else if(parameter == "Y") Y = value.toDouble();
            // else if(parameter == "Orientation") Orientation = value.toDouble();
            // else if(parameter == "TurretOrientation") TurretOrientation = value.toDouble();
            // else if(parameter == "Cooldown") Cooldown = value.toInt();
            // else if(parameter == "DestinationX") DestinationX = value.toDouble();
            // else if(parameter == "DestinationY") DestinationY = value.toDouble();
        }
        file.close();
        Orientation = DefaultOrientation;
    }
}

Skill *Minion::LoadAbility(const QString &Ability)
{
    // TODO: implement
    return nullptr;
}

void Minion::SetTeam(int team)
{
    Team = team;

    if(Team == 2)
    {
        if(PictureBase != nullptr)
        {
            QImage img = PictureBase->toImage().rgbSwapped();
            *PictureBase = QPixmap::fromImage(img);
        }
        if(PictureTurret != nullptr)
        {
            QImage img = PictureTurret->toImage().rgbSwapped();
            *PictureTurret = QPixmap::fromImage(img);
        }
    }
}

void Minion::MoveTick()
{
    if(++TicksCounter > Engine::TicksPerSecond) TicksCounter = 0;

    if(IsDead())
    {
        if(TimeToRespawn > 0)
        {
            if(!(Flags & IS_RESPAWNING) || !Engine::CheckCollision(this, X, Y, true))
                TimeToRespawn--;
        }
        else if(Flags & IS_WRECK)
        {
            Engine::GetRespawnPoint(Team, X, Y);
            TimeToRespawn = (5 + Level) * Engine::TicksPerSecond;
            Flags = (MinionFlags)(Flags & ~IS_WRECK);
            return; // skip rotation and movement
        }
        else if(!Flags & IS_RESPAWNING)
        {
            Flags = (MinionFlags)(Flags | IS_RESPAWNING);
            TimeToRespawn = (int)Engine::TicksPerSecond;
        }
        else if(!Engine::CheckCollision(this, X, Y, true))
        {
            Respawn();
        }
    }
    else
    {
        GainExperience();
        Regenerate();
    }

    if(Cooldown > 0) Cooldown--;

    UpdateDestinationPoint();

    if(!equal(DestinationX, X, 1.0) || !equal(DestinationY, Y, 1.0)) RotateAndMove();
}

void Minion::ShootTick()
{
    if(Flags & IS_WRECK) return;

    if(!IsDead()) UpdateDestinationMinion();

    double DesiredTurretOrientation;
    int DestinationMinionX = X;
    int DestinationMinionY = Y;

    UpdateDesiredTurretOrientation(DesiredTurretOrientation, DestinationMinionX, DestinationMinionY);

    Engine::Rotate(TurretOrientation, DesiredTurretOrientation, TurretRotationSpeed);

    if(DestinationMinion && Cooldown == 0 && !IsDead() &&
            equal(DesiredTurretOrientation, TurretOrientation, 0.01) &&
            Engine::GetDistance(this, DestinationMinion) <= Range)
    {
        Engine::SpawnBullet(Power, X, Y, DestinationMinionX, DestinationMinionY,
                            DestinationMinion, BulletSpeed,
                            PictureBullet, BulletFlags::GUIDED, 0);
        Cooldown = Engine::TicksPerSecond / FireRate;
    }
}

void Minion::UpdateDestinationMinion()
{
    if(!DestinationMinion || Flags&(IS_MINI|IS_TOWER))
    {
        double distance;
        Minion *Enemy = Engine::FindNearestEnemy(this, distance);
        if(Enemy != nullptr)
        {
            if(distance <= Range || (distance <= Sight && (Flags&IS_MINI)))
                NewTarget(Enemy, Flags&IS_MINI);
        }
        UpdateDestinationPoint();
    }
    else
    {
        double Distance = Engine::GetDistance(this, DestinationMinion);
        if(Distance > Range && (!(Flags & IS_FOLLOWING) || (Flags & IS_MINI)))
        {
            DestinationMinion = nullptr;
        }
    }
}
void Minion::UpdateDesiredTurretOrientation(double &DesiredTurretOrientation,
                                                int &DestinationMinionX, int &DestinationMinionY)
{
    if(DestinationMinion)
    {
        DestinationMinionX = DestinationMinion->GetX();
        DestinationMinionY = DestinationMinion->GetY();
        DesiredTurretOrientation = Engine::GetDestinationOrientation(X, Y,
                                                                         DestinationMinionX,
                                                                         DestinationMinionY);
    }
    else
    {
        if(!equal(DestinationX, X, 1.0) || !equal(DestinationY, Y, 1.0))
        {
            DesiredTurretOrientation = Engine::GetDestinationOrientation(X, Y,
                                                                             DestinationX,
                                                                             DestinationY);
        }
        else if(!equal(TurretOrientation, Orientation, 0.01))
        {
            DesiredTurretOrientation = Orientation;
        }
        else DesiredTurretOrientation = TurretOrientation = Orientation;
    }
}

void Minion::Damage(int Power, BulletFlags BFlags)
{
    if(!(BFlags & BulletFlags::IGNORE_ARMOR))
    {
        Power = Power * (10.0 - log(Armor+1))/10.0;
    }
    if(Power < 1) Power = 1;

    Health -= Power;
    if(IsDead())
    {
        if(Flags & IS_BASE)
        {
            if(GetTeam() == 1)
                Engine::GameStatistics.WinningTeam = 2;
            else
                Engine::GameStatistics.WinningTeam = 1;
            Engine::GameRunning = false;
        }
        if(!(Flags & (IS_MINI|IS_TOWER|IS_BASE)))
        {
            Flags = (MinionFlags)(Flags | IS_WRECK);
            TimeToRespawn = 5 * Engine::TicksPerSecond;

            DestinationMinion = nullptr;
            DestinationX = X;
            DestinationY = Y;
        }
        else
        {
            Engine::RemoveMinion(this);
        }
    }
}

void Minion::NewDestination(int NewX, int NewY)
{
    if(!(Flags & IS_WRECK))
    {
        Flags = (MinionFlags)(Flags & ~IS_FOLLOWING);
        DestinationX = NewX;
        DestinationY = NewY;
    }
}

void Minion::NewTarget(Minion *TargetMinion, bool Follow)
{
    if(!(Flags & IS_WRECK))
    {
        DestinationMinion = TargetMinion;
        if(Follow && TargetMinion != nullptr)
        {
            Flags = (MinionFlags)(Flags|IS_FOLLOWING);
            DestinationX = TargetMinion->GetX();
            DestinationY = TargetMinion->GetY();
        }
    }
}

void Minion::MinionRemovalNotification(Minion *RemovedMinion)
{
    if(DestinationMinion == RemovedMinion)
    {
        DestinationMinion = nullptr;
        if(Flags & IS_FOLLOWING)
        {
            DestinationX = X;
            DestinationY = Y;
            Flags = (MinionFlags)(Flags & ~IS_FOLLOWING);
        }
    }
}

void Minion::UpdateDestinationPoint()
{
    if(DestinationMinion )
    {
        if(DestinationMinion->IsDead())
        {
            DestinationMinion = nullptr;
            if(Flags & IS_FOLLOWING)
            {
                DestinationX = X;
                DestinationY = Y;
            }
        }
        else if(Flags & IS_FOLLOWING)
        {
            double distance = Engine::GetDistance(this, DestinationMinion);
            if(distance > Range * 0.95)
            {
                DestinationX = DestinationMinion->GetX();
                DestinationY = DestinationMinion->GetY();
            }
            else
            {
                DestinationX = X;
                DestinationY = Y;
            }
        }
    }
}

void Minion::RotateAndMove()
{
    double DestinationOrientation = Engine::GetDestinationOrientation(X, Y, DestinationX, DestinationY);

    bool Rotated = Engine::Rotate(Orientation, DestinationOrientation, RotationSpeed);

    if(!Rotated || (Flags & TURN_AND_MOVE))
    {
        if(!(Flags & TURN_AND_MOVE)) DestinationOrientation = Orientation;
        Engine::Move(this, DestinationX, DestinationY, DestinationOrientation);
    }
}

void Minion::Respawn()
{
    Flags = (MinionFlags)(Flags & ~(IS_WRECK|IS_RESPAWNING));
    Health = MaxHealth;
    Cooldown = 0;
}

void Minion::Spawn()
{
    Health = 0;
    TimeToRespawn = 0;
}

void Minion::GainExperience()
{
    if(TicksCounter != 0) return;

    if(DestinationMinion && !DestinationMinion->IsDead() &&
            Engine::GetDistance(this, DestinationMinion) <= Range)
    {
        if(DestinationMinion->IsStructure()) Experience += 4;
        else if(!DestinationMinion->IsMini()) Experience += 2;
        else Experience++;
    }
    else Experience++;

    if(Experience >= 30)
    {
        Experience = 0;
        Level++;

        Power += PowerFactor;
        Armor += ArmorFactor;
        MaxHealth += HealthFactor;
        Health += HealthFactor;
        FireRate += FireRateFactor;
        Sight += SightFactor;
        Range += RangeFactor;
        Speed += SpeedFactor;
        BulletSpeed += BulletSpeedFactor;
        RotationSpeed += RotationSpeedFactor;
        TurretRotationSpeed += TurretRotationSpeedFactor;
    }
}

void Minion::Regenerate()
{
    if(Flags & (IS_MINI|IS_BASE|IS_TOWER)) return;

    if(Health < MaxHealth) Health += 0.01 * MaxHealth / Engine::TicksPerSecond;
    if(Health > MaxHealth) Health = MaxHealth;
}
